import platform
import tkinter as tk
import shutil
from tkinter import ttk, filedialog, messagebox, scrolledtext
from typing import List, Dict, Any, Optional, Callable
import os
import json
from datetime import datetime
import hashlib
from concurrent.futures import ThreadPoolExecutor
import threading
import subprocess
from pathlib import Path
import psutil
import math
import ctypes  # 新增导入，用于Windows权限检查

class EnhancedScanResultsWindow(tk.Toplevel):
    """增强版扫描结果窗口，支持更详细的展示和选择操作"""
    
    def __init__(self, parent, scan_type, results, parent_app):
        super().__init__(parent)
        self.parent_app = parent_app
        self.title(f"{scan_type} 扫描结果详情")
        self.geometry("900x600")
        self.resizable(True, True)
        self.scan_type = scan_type
        self.results = results
        self.link_type = "delete"  # 默认处理方式为删除
        self.status_var = tk.StringVar()
        self.tree = None  # 初始化tree属性
        
        # 分页相关属性
        self.current_page = 1
        self.page_size = 100  # 每页显示数量
        self.total_items = 0
        self.total_pages = 1
        self.sorted_data = []  # 排序后的数据
        self.is_sorting = False  # 标记是否正在排序
        
        # 预定义排序变量
        self.sort_var = tk.StringVar(value="size")
        self.sort_reverse_var = tk.BooleanVar(value=True)

        # 创建UI
        self.create_widgets()
        
        # 窗口关闭时释放资源
        # 初始化数据
        self.process_results_data()
        
        self.bind_right_click()
        self.protocol("WM_DELETE_WINDOW", self.on_close)
        
    def create_widgets(self):
        # 内存状态显示
        mem_frame = ttk.Frame(self)
        mem_frame.pack(fill=tk.X, padx=10, pady=5)
        
        self.mem_var = tk.StringVar()
        self.mem_var.set(self.get_memory_status())
        ttk.Label(mem_frame, textvariable=self.mem_var).pack(side=tk.LEFT)
        
        # 每5秒更新一次内存状态
        self.after(5000, self.update_memory_status)

        # 顶部控制栏
        control_frame = ttk.Frame(self)
        control_frame.pack(fill=tk.X, padx=10, pady=5)
        
        # 分页控制栏
        self.create_pagination_controls()
        
        ttk.Label(control_frame, text="处理方式:").pack(side=tk.LEFT, padx=5)
        self.link_type_var = tk.StringVar(value="delete")  # 初始化默认值
        
        # 根据扫描类型动态设置处理方式选项
        if self.scan_type != "package":
            link_options = [("直接删除", "delete"), ("符号链接", "symlink"), ("硬链接", "hardlink")]
        else:
            link_options = [("直接删除", "delete")]  # 软件包仅显示删除
        
        for text, value in link_options:
            ttk.Radiobutton(
                control_frame, 
                text=text, 
                variable=self.link_type_var, 
                value=value
            ).pack(side=tk.LEFT, padx=5)
        
        ttk.Button(control_frame, text="全选", command=self.select_all).pack(side=tk.LEFT, padx=5)
        ttk.Button(control_frame, text="反选", command=self.invert_selection).pack(side=tk.LEFT, padx=5)
        ttk.Button(control_frame, text="应用处理", command=self.apply_action).pack(side=tk.RIGHT, padx=5)
        
        # 仅在非软件包扫描时添加排序控件
        if self.scan_type != "package":
            ttk.Label(control_frame, text="排序方式:").pack(side=tk.LEFT, padx=5)
            sort_options = [("大小", "size"), ("修改时间", "mtime"), ("路径", "path")]
            self.sort_var = tk.StringVar(value="size")
            for text, value in sort_options:
                ttk.Radiobutton(
                    control_frame, 
                    text=text, 
                    variable=self.sort_var, 
                    value=value,
                    command=self.sort_results
                ).pack(side=tk.LEFT, padx=5)
            
            self.sort_reverse_var = tk.BooleanVar(value=True)
            ttk.Checkbutton(
                control_frame, 
                text="降序", 
                variable=self.sort_reverse_var,
                command=self.sort_results
            ).pack(side=tk.LEFT, padx=5)
        
        # 结果展示区域
        self.create_results_display()
        
        # 底部状态栏
        status_frame = ttk.Frame(self)
        status_frame.pack(fill=tk.X, side=tk.BOTTOM, padx=10, pady=5)
        
        self.status_var = tk.StringVar()
        ttk.Label(status_frame, textvariable=self.status_var).pack(side=tk.LEFT)
        self.update_status()
    
    def create_pagination_controls(self):
        """创建分页控制栏"""
        pagination_frame = ttk.Frame(self)
        pagination_frame.pack(fill=tk.X, padx=10, pady=5)
        
        self.prev_btn = ttk.Button(pagination_frame, text="上一页", command=self.prev_page, state=tk.DISABLED)
        self.prev_btn.pack(side=tk.LEFT, padx=5)
        
        self.next_btn = ttk.Button(pagination_frame, text="下一页", command=self.next_page, state=tk.DISABLED)
        self.next_btn.pack(side=tk.LEFT, padx=5)
        
        ttk.Label(pagination_frame, text="页码:").pack(side=tk.LEFT, padx=5)
        
        self.page_var = tk.StringVar()
        self.page_entry = ttk.Entry(pagination_frame, textvariable=self.page_var, width=5)
        self.page_entry.pack(side=tk.LEFT, padx=5)
        
        ttk.Button(pagination_frame, text="跳转", command=self.goto_page).pack(side=tk.LEFT, padx=5)
        
        ttk.Label(pagination_frame, text="每页数量:").pack(side=tk.LEFT, padx=5)
        
        self.page_size_var = tk.StringVar(value=str(self.page_size))
        page_size_combo = ttk.Combobox(
            pagination_frame, 
            textvariable=self.page_size_var, 
            values=["50", "100", "200", "500"],
            width=5,
            state="readonly"
        )
        page_size_combo.pack(side=tk.LEFT, padx=5)
        page_size_combo.bind("<<ComboboxSelected>>", self.change_page_size)
        
        self.page_info = ttk.Label(pagination_frame, text="")
        self.page_info.pack(side=tk.RIGHT, padx=10)
    
    def get_memory_status(self) -> str:
        """获取当前内存使用情况"""
        try:
            mem = psutil.virtual_memory()
            return f"内存使用: {mem.percent}% (可用: {mem.available/1024/1024:.0f}MB)"
        except:
            return "内存状态: 未知"
    
    def update_memory_status(self):
        """更新内存状态显示"""
        self.mem_var.set(self.get_memory_status())
        self.after(5000, self.update_memory_status)
    
    def get_cache_dir(self) -> str:
        """获取剩余空间最大的磁盘分区作为缓存目录"""
        # 这里使用与config.py中相同的逻辑
        # 在实际应用中，应该从配置对象中获取
        return "C:\\" if platform.system() == "Windows" else "/"

    def bind_right_click(self):
        """绑定右键菜单事件"""
        if self.tree:
            self.tree.bind("<Button-3>", lambda e: self.show_context_menu(e))  # 明确绑定
            self.tree.bind("<Control-Button-1>", self.on_tree_click)  # Mac右键模拟

    def show_context_menu(self, event):
        """显示右键上下文菜单"""
        item = self.tree.identify_row(event.y)
        if not item:
            return

        # 获取文件路径
        file_path = self.tree.item(item, "values")[2]  # 注意：这里使用原始路径
        
        # 尝试从扫描结果中获取绝对路径
        abs_path = self.get_absolute_path_from_results(file_path)
        if not abs_path or not os.path.exists(abs_path):
            abs_path = file_path  # 回退到原始路径
        
        menu = tk.Menu(self, tearoff=0)

        # 通用操作
        menu.add_command(label="打开文件位置", command=lambda: self.open_file_location(abs_path))
        menu.add_command(label="查看文件属性", command=lambda: self.show_file_properties(abs_path))
        
        # 检查文件可访问性
        if os.path.exists(abs_path) and os.access(abs_path, os.R_OK):
            menu.add_command(label="删除文件", command=lambda: self.delete_single_file(abs_path))

            # 仅限重复文件扫描的额外操作
            if self.scan_type == "duplicate":
                menu.add_separator()
                menu.add_command(label="创建符号链接", 
                               command=lambda: self.create_link(abs_path, "symlink"))
                menu.add_command(label="创建硬链接",
                               command=lambda: self.create_link(abs_path, "hardlink"))
        else:
            menu.add_command(label="文件不可访问", state=tk.DISABLED)

        menu.post(event.x_root, event.y_root)

    def get_absolute_path_from_results(self, path):
        """从扫描结果中查找文件的绝对路径"""
        # 先尝试直接使用路径
        if os.path.exists(path):
            return path

        if self.scan_type == "duplicate":
            # 检查文件组
            for group in self.results.get('groups', []):
                for file in group.get('files', []):
                    if os.path.basename(file) == os.path.basename(path):
                        return file
            
            # 检查文件夹组
            for group in self.results.get('folder_groups', []):
                for folder in group.get('folders', []):
                    if os.path.basename(folder) == os.path.basename(path):
                        return folder
        elif self.scan_type == "package":
            for file_info in self.results.get('files', []):
                if os.path.basename(file_info.get('path', '')) == os.path.basename(path):
                    return file_info.get('path', '')
        return path  # 找不到时返回原始路径

    def open_file_location(self, file_path):
        """打开文件所在目录（增强版：处理路径问题）"""
        try:
            # 确保路径是绝对路径
            abs_path = os.path.normpath(file_path)
            
            # 检查路径是否存在
            if not os.path.exists(abs_path):
                # 尝试打开父目录
                parent_dir = os.path.dirname(abs_path)
                if os.path.exists(parent_dir):
                    abs_path = parent_dir
                else:
                    messagebox.showerror("错误", "文件或文件夹不存在")
                    return
            
            if os.name == 'nt':  # Windows
                cmd = f'explorer /select,"{os.path.normpath(abs_path)}"'
                subprocess.Popen(cmd, shell=True)
            else:  # Linux/Mac
                dir_path = os.path.dirname(abs_path)
                if platform.system() == 'Darwin':
                    script = f'''
                    tell application "Finder"
                        reveal POSIX file "{abs_path}"
                        activate
                    end tell
                    '''
                    subprocess.Popen(['osascript', '-e', script])
                else:
                    subprocess.Popen(['xdg-open', '--select', abs_path])
            
            if self.parent_app:
                self.parent_app.update_log(f"已打开文件位置: {abs_path}")
        except Exception as e:
            error_msg = f"无法打开目录: {abs_path} - {str(e)}"
            messagebox.showerror("错误", error_msg)
            if self.parent_app:
                self.parent_app.update_log(f"打开文件位置失败: {str(e)}")

    def show_file_properties(self, file_path):
        """显示文件属性窗口（增强版：处理路径问题）"""
        prop_win = tk.Toplevel(self)
        prop_win.title("文件属性")
        
        try:
            # 确保使用绝对路径
            abs_path = os.path.normpath(file_path)
            
            # 如果路径不存在，尝试查找原始路径
            if not os.path.exists(abs_path):
                original_path = self.find_original_path_in_results(abs_path)
                if original_path and os.path.exists(original_path):
                    abs_path = original_path
                else:
                    ttk.Label(prop_win, text=f"路径不存在: {abs_path}").pack()
                    return
            
            if os.path.exists(abs_path):
                stat = os.stat(abs_path)
                info = [
                    f"路径: {abs_path}",
                    f"大小: {self.format_size(stat.st_size)}",
                    f"创建时间: {datetime.fromtimestamp(stat.st_ctime).strftime('%Y-%m-%d %H:%M:%S')}",
                    f"修改时间: {datetime.fromtimestamp(stat.st_mtime).strftime('%Y-%m-%d %H:%M:%S')}",
                    f"访问时间: {datetime.fromtimestamp(stat.st_atime).strftime('%Y-%m-%d %H:%M:%S')}"
                ]
                
                for text in info:
                    ttk.Label(prop_win, text=text).pack(anchor=tk.W, padx=10, pady=5)
            else:
                ttk.Label(prop_win, text=f"路径不存在: {abs_path}").pack()
        except Exception as e:
            error_msg = f"无法获取属性: {str(e)}"
            ttk.Label(prop_win, text=error_msg).pack()

    def find_original_path_in_results(self, current_path):
        """在扫描结果中查找原始路径（用于解决路径不存在问题）"""
        if self.scan_type == "duplicate":
            # 检查文件组
            for group in self.results.get('groups', []):
                for file_path in group.get('files', []):
                    if os.path.abspath(file_path) == current_path:
                        return file_path
            
            # 检查文件夹组
            for group in self.results.get('folder_groups', []):
                for folder_path in group.get('folders', []):
                    if os.path.abspath(folder_path) == current_path:
                        return folder_path
        
        elif self.scan_type == "package":
            for file_info in self.results.get('files', []):
                path = file_info.get('path', '')
                if os.path.abspath(path) == current_path:
                    return path
        
        return None

    def delete_single_file(self, file_path):
        """删除单个文件（使用原始路径，但操作时使用绝对路径）"""
        abs_path = os.path.normpath(file_path)
        if not os.path.exists(abs_path):
            # 尝试查找原始路径
            original_path = self.find_original_path_in_results(abs_path)
            if original_path and os.path.exists(original_path):
                abs_path = original_path
            else:
                messagebox.showerror("错误", "文件或文件夹不存在")
                return
                
        if not messagebox.askyesno("确认删除", f"确定要删除以下文件吗？\n{abs_path}"):
            return
        
        try:
            if os.path.isfile(abs_path):
                os.remove(abs_path)
                if self.parent_app:
                    self.parent_app.update_log(f"成功删除: {abs_path}")
            elif os.path.isdir(abs_path):
                import shutil
                shutil.rmtree(abs_path)
                if self.parent_app:
                    self.parent_app.update_log(f"成功删除文件夹: {abs_path}")
            self.refresh_after_delete(abs_path)
        except Exception as e:
            error_msg = f"删除失败: {str(e)}"
            if self.parent_app:
                self.parent_app.update_log(error_msg)
            messagebox.showerror("删除失败", error_msg)

    def create_link(self, source_path, link_type):
        """创建链接文件（使用原始路径，但操作时使用绝对路径）"""
        abs_source = os.path.normpath(source_path)
        if not os.path.exists(abs_source):
            # 尝试查找原始路径
            original_path = self.find_original_path_in_results(abs_source)
            if original_path and os.path.exists(original_path):
                abs_source = original_path
            else:
                messagebox.showerror("错误", "源文件或文件夹不存在")
                return
        
        target_dir = filedialog.askdirectory(title="选择链接保存目录")
        if not target_dir:
            return
        
        try:
            file_name = os.path.basename(abs_source)
            link_path = os.path.join(target_dir, f"{file_name}_{link_type}")
            
            if link_type == "symlink":
                if os.name == 'nt' and not ctypes.windll.shell32.IsUserAnAdmin():  # type: ignore
                    raise PermissionError("需要管理员权限创建符号链接")
                os.symlink(abs_source, link_path)
            elif link_type == "hardlink":
                os.link(abs_source, link_path)
            
            messagebox.showinfo("成功", f"已创建{link_type}到: {link_path}")
            if self.parent_app:
                self.parent_app.update_log(f"已创建{link_type}到: {link_path}")
        except Exception as e:
            error_msg = f"无法创建{link_type}: {str(e)}"
            messagebox.showerror("创建失败", error_msg)
            if self.parent_app:
                self.parent_app.update_log(f"创建{link_type}失败: {str(e)}")

    def refresh_after_delete(self, deleted_path):
        """删除后刷新列表"""
        for item in self.tree.get_children():
            if self.tree.item(item, "values")[2] == deleted_path:
                self.tree.delete(item)
                
                # 尝试从磁盘删除缓存文件（如果存在）
                try:
                    cache_path = os.path.join(self.get_cache_dir(), ".cleaner_cache", os.path.basename(deleted_path))
                    if os.path.exists(cache_path):
                        os.remove(cache_path)
                        if self.parent_app:
                            self.parent_app.update_log(f"已删除缓存文件: {cache_path}")
                except Exception as e:
                    if self.parent_app:
                        self.parent_app.update_log(f"删除缓存失败: {str(e)}")
                break
        self.update_status()
        if self.parent_app:
            self.parent_app.update_log(f"已刷新列表，删除: {deleted_path}")

    def apply_action(self):
        """应用选定的处理方式到选中的文件"""
        selected_files = self.get_selected_files()
        if not selected_files:
            messagebox.showinfo("提示", "请先选择要处理的文件")
            return
            
        action_type = self.link_type_var.get()
        if action_type == "delete":
            if not messagebox.askyesno("确认删除", "确定要删除选中的文件吗？此操作不可恢复！"):
                return
        else:
            if not messagebox.askyesno("确认操作", f"确定要对选中的文件创建{action_type}吗？"):
                return
                
        success_count = 0
        failed_count = 0
        failed_files = []
        
        for file_path in selected_files:
            try:
                abs_path = self.get_absolute_path_from_results(file_path)
                if not abs_path:
                    abs_path = file_path
                    
                abs_path = os.path.normpath(abs_path)
                
                # 检查文件是否存在
                if not os.path.exists(abs_path):
                    # 尝试查找原始路径
                    original_path = self.find_original_path_in_results(abs_path)
                    if original_path and os.path.exists(original_path):
                        abs_path = original_path
                    else:
                        raise FileNotFoundError(f"文件不存在: {abs_path}")
                
                if action_type == "delete":
                    if os.path.isfile(abs_path):
                        os.remove(abs_path)
                    elif os.path.isdir(abs_path):
                        import shutil
                        shutil.rmtree(abs_path)
                    success_count += 1
                elif action_type == "symlink":
                    # 符号链接逻辑（需要目标文件存在）
                    target_dir = os.path.dirname(abs_path)
                    link_name = os.path.basename(abs_path) + ".link"
                    link_path = os.path.join(target_dir, link_name)
                    
                    # 检查是否有足够权限创建符号链接
                    if os.name == 'nt':  # Windows系统
                        # Windows需要管理员权限或启用开发人员模式
                        try:
                            if not ctypes.windll.shell32.IsUserAnAdmin():  # type: ignore
                                messagebox.showwarning("权限不足", "在Windows上创建符号链接需要管理员权限")
                                failed_count += 1
                                failed_files.append(abs_path)
                                continue
                        except:
                            pass
                    
                    os.symlink(abs_path, link_path)
                    success_count += 1
                elif action_type == "hardlink":
                    # 硬链接逻辑
                    target_dir = os.path.dirname(abs_path)
                    link_name = os.path.basename(abs_path) + ".hardlink"
                    link_path = os.path.join(target_dir, link_name)
                    
                    os.link(abs_path, link_path)
                    success_count += 1
                    
            except Exception as e:
                failed_count += 1
                error_msg = f"处理失败: {abs_path} - {str(e)}"
                failed_files.append(error_msg)
                if self.parent_app:
                    self.parent_app.update_log(error_msg)
        
        # 显示处理结果
        result_msg = f"操作完成！成功: {success_count}, 失败: {failed_count}"
        if failed_count > 0:
            result_msg += "\n\n失败文件列表:\n" + "\n".join(failed_files)
            
        messagebox.showinfo("处理结果", result_msg)
        self.destroy()  # 关闭窗口

    def get_selected_files(self):
        """获取所有选中的文件（返回原始路径）"""
        selected_files = []
        for item_id in self.tree.get_children():
            # 检查是否是文件节点（不是组节点）
            if self.tree.item(item_id, "values") and len(self.tree.item(item_id, "values")) > 0:
                if self.tree.item(item_id, "values")[0] == "✓":
                    file_path = self.tree.item(item_id, "values")[2]
                    selected_files.append(file_path)
        return selected_files
  
    
    def create_results_display(self):
        """根据扫描类型创建相应的结果展示"""
        if self.scan_type == "duplicate":
            self.create_duplicate_results_display()
        elif self.scan_type == "package":
            self.create_package_results_display()
    
    def create_duplicate_results_display(self):
        ttk.Label(self, text="重复文件扫描结果", font=("Arial", 14, "bold")).pack(pady=10)
        
        groups = self.results.get('groups', [])
        folder_groups = self.results.get('folder_groups', [])
        
        # 确保始终创建 Treeview
        columns = ("select", "size", "path")
        self.tree = ttk.Treeview(self, columns=columns, show="tree headings")
        self.tree.heading("select", text="选择")
        self.tree.heading("size", text="大小")
        self.tree.heading("path", text="文件路径")
        self.tree.column("select", width=50, anchor=tk.CENTER)
        self.tree.column("size", width=100)
        self.tree.column("path", width=500, stretch=tk.YES)
        self.tree.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 添加滚动条
        scrollbar = ttk.Scrollbar(self.tree, orient=tk.VERTICAL, command=self.tree.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.tree.configure(yscrollcommand=scrollbar.set)
        
        # 设置样式
        style = ttk.Style()
        if groups or folder_groups:
            style.configure("Treeview", rowheight=25)
            style.configure("Treeview.Heading", font=("Arial", 10, "bold"))
            
            # 配置标签样式
            self.tree.tag_configure("main", foreground="green")
            self.tree.tag_configure("duplicate", foreground="red")
            self.tree.tag_configure("folder", foreground="blue")
            self.tree.tag_configure("outer", font=("Arial", 10, "bold"))
        else:
            # 添加空结果提示
            self.tree.insert("", tk.END, values=("", "无结果", "未发现重复文件或文件夹"))
        
        # 绑定选择事件
        self.tree.bind("<Button-1>", self.on_tree_click)

    def create_package_results_display(self):
        ttk.Label(self, text="软件包扫描结果", font=("Arial", 14, "bold")).pack(pady=10)
        
        files = self.results.get('files', [])
        
        # 确保始终创建 Treeview
        columns = ("select", "size", "path", "mtime")
        self.tree = ttk.Treeview(self, columns=columns, show="headings")
        self.tree.heading("select", text="选择")
        self.tree.heading("size", text="大小")
        self.tree.heading("path", text="文件路径")
        self.tree.heading("mtime", text="修改时间")
        self.tree.column("select", width=50, anchor=tk.CENTER)
        self.tree.column("size", width=100)
        self.tree.column("path", width=500, stretch=tk.YES)
        self.tree.column("mtime", width=150)
        self.tree.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 添加滚动条
        scrollbar = ttk.Scrollbar(self.tree, orient=tk.VERTICAL, command=self.tree.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.tree.configure(yscrollcommand=scrollbar.set)

        # 设置样式
        style = ttk.Style()
        if files:
            style.configure("Treeview", rowheight=25)
            style.configure("Treeview.Heading", font=("Arial", 10, "bold"))
        else:
            # 添加空结果提示
            self.tree.insert("", tk.END, values=("", "", "未发现可清理的软件包", ""))
        
        # 绑定选择事件
        self.tree.bind("<Button-1>", self.on_tree_click)      

    def process_results_data(self):
        """处理扫描结果数据，准备分页显示"""
        if self.scan_type == "duplicate":
            self.process_duplicate_data()
        elif self.scan_type == "package":
            self.process_package_data()
        
        # 初始排序
        self.sort_results()
        
        # 显示第一页数据
        self.display_current_page()
    
    def process_duplicate_data(self):
        """处理重复文件扫描结果""" 
        self.original_data = []
        groups = self.results.get('groups', [])
        folder_groups = self.results.get('folder_groups', []) if 'folder_groups' in self.results else []
        
        # 处理文件组
        for group_idx, group in enumerate(groups):
            group_files = []
            for file_idx, file_path in enumerate(group.get('files', [])):
                try:
                    # 使用原始路径（不转换为绝对路径）
                    if os.path.exists(file_path):
                        size = os.path.getsize(file_path) if os.path.exists(file_path) else 0
                        mtime = os.path.getmtime(file_path)
                        group_files.append({
                            'path': file_path,
                            'size': size,
                            'mtime': mtime,
                            'is_original': (file_idx == 0)  # 标记是否保留文件
                        })
                except Exception as e:
                    self.update_status(f"处理文件出错: {str(e)}")
            
            # 添加整个组
            if group_files:
                self.original_data.append({
                    'type': 'file_group', 
                    'group_idx': group_idx,
                    'hash': group.get('hash', ''),
                    'total_size': group.get('total_size', 0),
                    'file_count': len(group_files),
                    'files': group_files
                })
        
        # 处理文件夹组（使用新结构）
        for group_idx, group_info in enumerate(folder_groups):
            group_folders = []
            folders = group_info.get('folders', [])
            # 找出最外层目录（最短路径）
            outer_dir = min(folders, key=len) if folders else ""
            
            for folder_idx, folder_path in enumerate(folders):
                try:
                    if os.path.exists(folder_path):
                        size = group_info.get('total_size', 0) / max(1, len(folders))
                        mtime = os.path.getmtime(folder_path)
                        group_folders.append({
                            'path': folder_path,
                            'size': size,
                            'mtime': mtime,
                            'is_original': (folder_idx == 0),  # 标记是否保留文件夹
                            'is_folder': True,
                            'is_outer': (folder_path == outer_dir)  # 新增字段：是否是最外层目录
                        })
                except Exception as e:
                    self.update_status(f"处理文件夹出错: {str(e)}")
            
            # 添加整个组
            if group_folders:
                self.original_data.append({
                    'type': 'folder_group',
                    'group_idx': group_idx,
                    'total_size': group_info.get('total_size', 0),
                    'outer_dir': outer_dir,
                    'folder_count': len(group_folders),
                    'files': group_folders
                })
        
        self.total_items = len(self.original_data)
        self.total_pages = max(1, math.ceil(self.total_items / self.page_size))
        self.update_pagination_info()
    
    def process_package_data(self):
        """处理软件包扫描结果"""
        self.original_data = []
        files = self.results.get('files', [])
        
        for file_info in files:
            try:
                if isinstance(file_info, dict):
                    file_path = file_info.get('path', '')
                    if os.path.exists(file_path):
                        size = file_info.get('size', 0)
                        mtime = file_info.get('mtime', 0)
                        self.original_data.append({
                            'path': file_path,
                            'size': size,
                            'mtime': mtime
                        })
            except:
                pass
        
        self.total_items = len(self.original_data)
        self.total_pages = max(1, math.ceil(self.total_items / self.page_size))
        self.update_pagination_info()
    
    def update_pagination_info(self):
        """更新分页信息"""
        if self.total_items == 0:
            self.page_info.config(text="没有可显示的项目")
            self.prev_btn.config(state=tk.DISABLED)
            self.next_btn.config(state=tk.DISABLED)
            return
        
        # 启用/禁用翻页按钮
        self.prev_btn.config(state=tk.NORMAL if self.current_page > 1 else tk.DISABLED)
        self.next_btn.config(state=tk.NORMAL if self.current_page < self.total_pages else tk.DISABLED)
        self.page_var.set(str(self.current_page))
        self.page_info.config(text=f"第 {self.current_page}/{self.total_pages} 页")
    
    def display_current_page(self):
        """显示当前页的数据"""
        if not self.tree:
            return
            
        # 清空Treeview
        for item in self.tree.get_children():
            self.tree.delete(item)
        
        if self.total_items == 0:
            return
        
        # 计算当前页的数据范围
        start_idx = (self.current_page - 1) * self.page_size
        end_idx = min(start_idx + self.page_size, self.total_items)
        self.page_info.config(text=f"第 {start_idx+1}-{end_idx} 条，共 {self.total_items} 条")

        # 加载当前页数据
        for data in self.sorted_data[start_idx:end_idx]:
            if self.scan_type == "package":
                # 软件包数据
                self.add_package_row(data)
            else:
                # 重复文件/文件夹数据
                self.add_group_row(data)
    
    def add_group_row(self, group_data):
        """添加分组到Treeview"""
        if group_data['type'] == 'file_group':
            group_text = f"重复文件组 #{group_data['group_idx']+1} (哈希: {group_data['hash'][:8]}...)"
        else:  # folder_group
            group_text = f"重复文件夹组 #{group_data['group_idx']+1}"
        
        group_info = f"{group_data['file_count'] if 'file_count' in group_data else group_data['folder_count']}个文件/文件夹, " \
                     f"总计 {self.format_size(group_data['total_size'])}"
        
        # 创建组节点
        group_node = self.tree.insert("", tk.END, text=group_text,
                                     values=("", self.format_size(group_data['total_size']), group_info))
        
        # 添加组内文件
        for file_data in group_data.get('files', []):
            # 确定标签（颜色）
            tags = ()
            if file_data.get('is_folder', False):
                prefix = "[文件夹] "
                tags = ("folder",)
            else:
                prefix = ""
                
            if file_data.get('is_original', False):
                tags = ("main",) + tags
            else:
                tags = ("duplicate",) + tags
            
            # 如果是文件夹组的最外层目录，加粗显示
            if file_data.get('is_outer', False):
                tags = tags + ("outer",)
            
            # 添加文件节点
            self.tree.insert(group_node, tk.END, 
                            values=("✓", self.format_size(file_data['size']), prefix + file_data['path']),
                            tags=tags)
    
    def add_package_row(self, data):
        """添加软件包行到Treeview"""
        file_path = data['path']
        size = data['size']
        try:
            mtime = datetime.fromtimestamp(float(data['mtime'])).strftime("%Y-%m-%d %H:%M:%S")
        except:
            mtime = "时间无效"
            
        self.tree.insert("", tk.END, values=("✓", self.format_size(size), file_path, mtime))
    
    def sort_results(self):
        """根据选择的排序方式对结果进行排序"""
        if not hasattr(self, 'original_data'):
            return
            
        sort_key = self.sort_var.get()
        reverse = self.sort_reverse_var.get()
        
        if self.is_sorting:
            return
            
        self.is_sorting = True
        # 清空当前显示
        for item in self.tree.get_children():
            self.tree.delete(item)
        
        # 在后台线程中执行排序
        threading.Thread(
            target=self._perform_sorting,
            args=(sort_key, reverse),
            daemon=True
        ).start()
    
    def _perform_sorting(self, sort_key, reverse):
        """实际排序逻辑"""
        try:
            # 复制数据避免主线程冲突
            data_to_sort = list(self.original_data)
            
            # 根据扫描类型选择排序方式
            if self.scan_type == "package":
                # 软件包扫描结果排序
                if sort_key == "size":
                    key_func = lambda x: x['size']
                elif sort_key == "mtime":
                    key_func = lambda x: x['mtime']
                else:  # path
                    key_func = lambda x: x['path'].lower()
            else:
                # 重复文件扫描结果排序
                if sort_key == "size":
                    key_func = lambda x: x['total_size']
                elif sort_key == "mtime":
                    # 使用组内第一个文件的修改时间
                    key_func = lambda x: x['files'][0]['mtime'] if x['files'] else 0
                else:  # path
                    # 使用组内第一个文件的路径
                    key_func = lambda x: x['files'][0]['path'].lower() if x['files'] else ""
            
            # 执行排序
            self.sorted_data = sorted(
                data_to_sort,
                key=key_func,
                reverse=reverse
            )
            
            # 回到主线程更新UI
            self.after(0, self._update_after_sort)
        except Exception as e:
            error_msg = f"排序错误: {str(e)}"
            self.after(0, lambda: self.status_var.set(error_msg))
    
    def _update_after_sort(self):
        """排序完成后更新UI"""
        self.current_page = 1
        self.display_current_page()
        self.update_status()
        self.status_var.set("排序完成")
        self.is_sorting = False
    
    def on_tree_click(self, event):
        """处理Treeview中的点击事件，特别是选择框的切换"""
        region = self.tree.identify_region(event.x, event.y)
        if region == "cell":
            column = int(self.tree.identify_column(event.x).replace('#', ''))
            if column == 1:  # 选择列
                item_id = self.tree.identify_row(event.y)
                current_value = self.tree.item(item_id, "values")[0] if self.tree.item(item_id, "values") else ""
                if current_value:  # 确保是文件节点（组节点没有选择框）
                    new_value = "✓" if current_value != "✓" else ""
                    self.tree.set(item_id, column=0, value=new_value)
                    self.update_status()
    
    def prev_page(self):
        """跳转到上一页"""
        if self.current_page > 1:
            self.current_page -= 1
            self.update_pagination_info()
            self.display_current_page()
    
    def next_page(self):
        """跳转到下一页"""
        if self.current_page < self.total_pages:
            self.current_page += 1
            self.update_pagination_info()
            self.display_current_page()
    
    def goto_page(self):
        """跳转到指定页码"""
        try:
            page = int(self.page_var.get())
            if 1 <= page <= self.total_pages:
                self.current_page = page
                self.update_pagination_info()
                self.display_current_page()
        except ValueError:
            pass
    
    def change_page_size(self, event=None):
        """改变每页显示数量"""
        try:
            new_size = int(self.page_size_var.get())
            if new_size > 0:
                self.page_size = new_size
                self.total_pages = max(1, math.ceil(self.total_items / self.page_size))
                
                # 确保当前页在有效范围内
                if self.current_page > self.total_pages:
                    self.current_page = self.total_pages
                
                self.update_pagination_info()
                self.display_current_page()
        except ValueError:
            pass
    
    def select_all(self):
        """全选所有可清理的项目"""
        for item_id in self.tree.get_children():
            # 只处理文件节点（不是组节点）
            if self.tree.item(item_id, "values") and len(self.tree.item(item_id, "values")) > 0:
                tags = self.tree.item(item_id, "tags")
                if "main" not in tags:  # 不选择保留的文件
                    self.tree.set(item_id, column=0, value="✓")
        self.update_status()
    
    def invert_selection(self):
        """反选所有项目"""
        for item_id in self.tree.get_children():
            # 只处理文件节点（不是组节点）
            if self.tree.item(item_id, "values") and len(self.tree.item(item_id, "values")) > 0:
                tags = self.tree.item(item_id, "tags")
                if "main" not in tags:  # 不操作保留的文件
                    current_value = self.tree.item(item_id, "values")[0]
                    new_value = "✓" if current_value != "✓" else ""
                    self.tree.set(item_id, column=0, value=new_value)
        self.update_status()
    
    def update_status(self):
        """更新状态栏信息"""
        selected_count = 0
        total_size = 0

        for item_id in self.tree.get_children():
            # 只处理文件节点（不是组节点）
            if self.tree.item(item_id, "values") and len(self.tree.item(item_id, "values")) > 0:
                if self.tree.item(item_id, "values")[0] == "✓":
                    selected_count += 1
                    size_str = self.tree.item(item_id, "values")[1]
                    try:
                        # 解析大小字符串，转换为字节
                        size_parts = size_str.split()
                        if len(size_parts) == 2:
                            size_num = float(size_parts[0])
                            size_unit = size_parts[1]
                            if size_unit == "KB":
                                size_num *= 1024
                            elif size_unit == "MB":
                                size_num *= 1024 * 1024
                            elif size_unit == "GB":
                                size_num *= 1024 * 1024 * 1024
                            total_size += size_num
                    except:
                        pass

        # 使用当前窗口的status_var更新状态
        self.status_var.set(f"已选择 {selected_count} 个项目，释放空间: {total_size / 1024 / 1024:.2f} MB")
    
    def format_size(self, size_bytes):
        """将字节转换为更友好的格式"""
        if size_bytes < 1024:
            return f"{size_bytes} B"
        elif size_bytes < 1024 * 1024:
            return f"{size_bytes/1024:.2f} KB"
        elif size_bytes < 1024 * 1024 * 1024:
            return f"{size_bytes/1024/1024:.2f} MB"
        else:
            return f"{size_bytes/1024/1024/1024:.2f} GB"
    
    def on_close(self):
        """关闭窗口时的清理操作"""
        self.destroy()